assert(PandaDKPMaster, "PandaDKPMaster not found!")

------------------------------
--      Are you local?      --
------------------------------
local PandaDKPMaster = PandaDKPMaster
local Players =
{
   UI = {},
}
PandaDKPMaster.Players = Players

local dewdrop = AceLibrary("Dewdrop-2.5")


function Players:Enable()
   Players:SetupUI()
end


function Players:Class(player)
   if (nil ~= PandaDKPMaster.db.realm.DKP.Players[player]) then
      return PandaDKPMaster.db.realm.DKP.Players[player].Class
   end
   
   return Players:RaidMembers()[player]
end


function Players:SetPlayerClass(player, class)
   if (nil ~= PandaDKPMaster.db.realm.DKP.Players[player]) then
      PandaDKPMaster.db.realm.DKP.Players[player].Class = class
   end
end


function Players:GetDKP(player, pool)
   if (nil == PandaDKPMaster.db.realm.DKP.Players[player]) then return 0 end
   if (nil == PandaDKPMaster.db.realm.DKP.Players[player].Pools[pool]) then return 0 end
   
   return PandaDKPMaster.db.realm.DKP.Players[player].Pools[pool]
end


function Players:GetHistoricalDKP(player, pool)
   if (nil == PandaDKPMaster.db.realm.DKP.HistoricalDKP[player]) then return 0 end
   if (nil == PandaDKPMaster.db.realm.DKP.HistoricalDKP[player][pool]) then return 0 end
   
   return PandaDKPMaster.db.realm.DKP.HistoricalDKP[player][pool]
end


function Players:RaidMembers()
   local members = {}
   for i=1, GetNumRaidMembers() do
      local name, rank, subgroup, level, class = GetRaidRosterInfo(i)
      members[name] = class
   end
   
   return members
end


function Players:AllPlayers()
   local players = {}
   for player, record in pairs(PandaDKPMaster.db.realm.DKP.Players) do
      tinsert(players, player)
   end
   
   return players
end


function Players:AddDKP(player, dkp, pool)
   dkp = tonumber(dkp)
   if (nil == PandaDKPMaster.db.realm.DKP.Players[player]) then
      PandaDKPMaster.db.realm.DKP.Players[player] =
      {
         Pools = {},
         Class = Players:Class(player),
      }
   end
   if (nil == PandaDKPMaster.db.realm.DKP.Players[player].Pools[pool]) then
      PandaDKPMaster.db.realm.DKP.Players[player].Pools[pool] = 0.0
   end
   
   PandaDKPMaster.db.realm.DKP.Players[player].Pools[pool] = PandaDKPMaster.db.realm.DKP.Players[player].Pools[pool] + dkp
end


function Players:AddHistoricalDKP(player, dkp, pool)
   if (nil == PandaDKPMaster.db.realm.DKP.HistoricalDKP[player]) then PandaDKPMaster.db.realm.DKP.HistoricalDKP[player] = {} end
   if (nil == PandaDKPMaster.db.realm.DKP.HistoricalDKP[player][pool]) then PandaDKPMaster.db.realm.DKP.HistoricalDKP[player][pool] = 0 end
   
   dkp = tonumber(dkp)
   PandaDKPMaster.db.realm.DKP.HistoricalDKP[player][pool] =
    PandaDKPMaster.db.realm.DKP.HistoricalDKP[player][pool] + dkp
end


function Players:InternalPurchaseItem(player, itemId, dkp, pool, lootNum, needOrGreed)
   itemId = tostring(itemId)
   dkp = tonumber(dkp)
   
   -- Update the database
   local ts = PandaDKPMaster:GetTimestamp()
   PandaDKPMaster.Events:InsertPurchaseItemEvent(ts, pool, dkp, itemId, player, lootNum, needOrGreed)
   
   tinsert(PandaDKPMaster.db.realm.DKP.Logging, string.format("Internal Purchase"))

   -- Send a message to the guild to other officers
   local message = PandaDKPMaster.Messages:CreatePIMessage(ts, pool, dkp, itemId, player, lootNum, needOrGreed)
   local md5 = PandaDKPMaster.MD5Lib:MD5(message)
   tinsert(PandaDKPMaster.db.realm.DKP.Logging, string.format("Sending message %s (%s)", message, md5))
   PandaDKPMaster.Comm:Send("GUILD", message)
   
   -- Send a message to all users
   local _, itemLink = GetItemInfo(itemId)
   message = PandaDKPMaster.Messages:CreatePurchaseMessage(pool, dkp, itemLink, player)
   PandaDKPMaster.Comm:SendClient("GUILD", message)
end


-- Called when a PI message is seen
function Players:OnPurchaseItemMessage(timestamp, player, itemId, dkp, pool, lootNum, needOrGreed)
   -- MD5 the message
   local message = PandaDKPMaster.Messages:CreatePIMessage(timestamp, pool, dkp, itemId, player, lootNum, needOrGreed)
   local md5 = PandaDKPMaster.MD5Lib:MD5(message)
   tinsert(PandaDKPMaster.db.realm.DKP.Logging, string.format("Received message %s (%s)", message, md5))
   
   -- If we've already seen it, skip it
   if (nil ~= PandaDKPMaster.db.realm.DKP.Events[md5]) then return end
   
   -- Add the event
   tinsert(PandaDKPMaster.db.realm.DKP.Logging, string.format("Purchase Item message inserted"))
   PandaDKPMaster.Events:InsertPurchaseItemEvent(timestamp, pool, dkp, itemId, player, lootNum, needOrGreed)
end



function Players:PublishDKPToGuild()
   for name, data in pairs(PandaDKPMaster.db.realm.DKP.Players) do
      local pools = {}
      for i, pool in pairs(PandaDKPMaster.db.realm.DKP.Pools) do
         tinsert(pools, string.format('"%s"=%s', pool, PandaDKPMaster.Players:GetDKP(name, pool)))
      end
      local class = data.Class or "Priest"
      local message = string.format('DKP %s "%s" {%s}', name, class, table.concat(pools, ' '))
      PandaDKPMaster.Comm:SendClient("GUILD", message)
   end
end


function Players:PublishDKPToRaid(pool)
   Players.ToPublish = {}
   for name, data in pairs(PandaDKPMaster.db.realm.DKP.Players) do
      for poolname, dkp in pairs(data.Pools) do
         if (poolname == pool) then
            tinsert(Players.ToPublish, { name = name, dkp = dkp, class = data.Class })
         end
      end
   end
   
   -- Sort in reverse order because we will pop
   sort(Players.ToPublish, function(a,b) return a.name > b.name end)
   
   SendChatMessage(string.format("DKP values in the %s pool are:", pool), "RAID")
   Players.PublishTimer = PandaDKPMaster:ScheduleRepeatingTimer(function() PandaDKPMaster.Players:PublishTimerFired() end, 1)
   Players:PublishTimerFired()
end

function Players:PublishTimerFired()
   -- Send 5 messages
   local count = 50
   while count > 0 do
      if (0 == #(Players.ToPublish)) then PandaDKPMaster:CancelTimer(Players.PublishTimer) return end
      local player = tremove(Players.ToPublish)
      local hex = PandaDKPMaster:getClassHex(player.class)
      --SendChatMessage(string.format("|cff%s%s|r (%s)", hex, player.name, player.dkp), "RAID")
      SendChatMessage(string.format("   %s: %s", player.name, player.dkp), "RAID")
      count = count - 1
   end
end












----------------------
--   Interface UI   --
----------------------

function Players:SetupUI()
   Players.UI.SelectedPool = PandaDKPMaster.db.realm.DKP.Pools[1]
   Players.UI.ClassFilter = "Show All"
   Players.UI.SortOrder = { "player", "class", "dkp" }
   
   UIDropDownMenu_Initialize(PDKPM_PlayersConfigPoolMenu, function() PandaDKPMaster.Players:InitPoolsDropdown() end)
   UIDropDownMenu_Initialize(PDKPM_PlayersConfigClassMenu, function() PandaDKPMaster.Players:InitClassDropdown() end)
   
   PDKPM_PlayersConfigListDKPPlayerHeaderTitle:SetText("Player")
   PDKPM_PlayersConfigListDKPPlayerHeader.sorter = "player"
   PDKPM_PlayersConfigListDKPClassHeaderTitle:SetText("Class")
   PDKPM_PlayersConfigListDKPClassHeader.sorter = "class"
   PDKPM_PlayersConfigListDKPDKPHeaderTitle:SetText("DKP")
   PDKPM_PlayersConfigListDKPDKPHeader.sorter = "dkp"
   
   Players:RefilterDKPList()
end


function Players:ConfigPanelOnShow()
   Players:RefilterDKPList()
end

function Players:ConfigPanelOnHide()
end


function Players:InitPoolsDropdown()
   local info = UIDropDownMenu_CreateInfo()
   info.owner = PDKPM_PlayersConfigPoolMenu
   info.func = function() PandaDKPMaster.Players:PoolClicked(this) end
   info.checked = nil
   
   for i, pool in ipairs(PandaDKPMaster.db.realm.DKP.Pools) do
      info.text = pool
      info.value = pool
      UIDropDownMenu_AddButton(info, 1)
   end
   
   UIDropDownMenu_SetSelectedValue(PDKPM_PlayersConfigPoolMenu, Players.UI.SelectedPool)
end

function Players:PoolClicked(info)
   Players.UI.SelectedPool = info.value
   UIDropDownMenu_SetSelectedValue(PDKPM_PlayersConfigPoolMenu, Players.UI.SelectedPool)
   Players:RefilterDKPList()
end

function Players:InitClassDropdown()
   local info = UIDropDownMenu_CreateInfo()
   info.owner = PDKPM_PlayersConfigClassMenu
   info.func = function() PandaDKPMaster.Players:ClassFilterClicked(this) end
   info.checked = nil
   
   info.text = "Show All"
   info.value = "Show All"
   UIDropDownMenu_AddButton(info, 1)
   info.text = ""
   info.value = ""
   info.disabled = true
   UIDropDownMenu_AddButton(info, 1)
   info.disabled = false
   
   local SetText = function(info, text)
      info.text = string.format("|cff%s%s|r", PandaDKPMaster:getClassHex(text), text)
      info.value = text
      UIDropDownMenu_AddButton(info, 1)
   end
   
   SetText(info, "Death Knight")
   SetText(info, "Druid")
   SetText(info, "Hunter")
   SetText(info, "Mage")
   SetText(info, "Paladin")
   SetText(info, "Priest")
   SetText(info, "Rogue")
   SetText(info, "Shaman")
   SetText(info, "Warlock")
   SetText(info, "Warrior")
   
   UIDropDownMenu_SetSelectedValue(PDKPM_PlayersConfigClassMenu, Players.UI.ClassFilter)
end

function Players:ClassFilterClicked(info)
   Players.UI.ClassFilter = info.value
   UIDropDownMenu_SetSelectedValue(PDKPM_PlayersConfigClassMenu, Players.UI.ClassFilter)
   Players:RefilterDKPList()
end

function Players:RefilterDKPList()
   Players.UI.DKPList = {}
   -- Add a DKP record for every item that meets the filter
   for player, playerRec in pairs(PandaDKPMaster.db.realm.DKP.Players) do
      if ("Show All" == Players.UI.ClassFilter or playerRec.Class == Players.UI.ClassFilter) then
         local class = playerRec.Class
         if nil == class then class = '<unknown>' end
         local record =
         {
            player = player,
            class = class,
            dkp = Players:GetDKP(player, Players.UI.SelectedPool),
         }
         tinsert(Players.UI.DKPList, record)
      end
   end
   
   -- Sort the list according to the sort order
   local function SortDKP(a, b)
      if (a[Players.UI.SortOrder[1]] == b[Players.UI.SortOrder[1]]) then
         if (a[Players.UI.SortOrder[2]] == b[Players.UI.SortOrder[2]]) then
            return a[Players.UI.SortOrder[3]] < b[Players.UI.SortOrder[3]]
         else
            return a[Players.UI.SortOrder[2]] < b[Players.UI.SortOrder[2]]
         end
      else
         return a[Players.UI.SortOrder[1]] < b[Players.UI.SortOrder[1]]
      end
   end
   sort(Players.UI.DKPList, SortDKP)
   
   Players:DKPListUpdate()
end



function Players:DKPListUpdate()
   local numEntries = #(Players.UI.DKPList)
   FauxScrollFrame_Update(PDKPM_PlayersConfigListScrollFrame, numEntries, 16, 16, nil, 350, 350, nil, 350, 350, true)
   
   local line
   local offset
   
   for line = 1, 16 do
      offset = line + FauxScrollFrame_GetOffset(PDKPM_PlayersConfigListScrollFrame)
      local button = getglobal("PDKPM_PlayersConfigListDKPLine"..line)
      button:SetID(offset)
      if (offset <= numEntries) then
         local hex = PandaDKPMaster:getClassHex(Players.UI.DKPList[offset].class)
         local text = getglobal("PDKPM_PlayersConfigListDKPLine"..line.."Player")
         text:SetText(string.format("|cff%s%s|r", hex, Players.UI.DKPList[offset].player))
         text = getglobal("PDKPM_PlayersConfigListDKPLine"..line.."Class")
         text:SetText(string.format("|cff%s%s|r", hex, Players.UI.DKPList[offset].class))
         text = getglobal("PDKPM_PlayersConfigListDKPLine"..line.."DKP")
         text:SetText(string.format("%.2f", Players.UI.DKPList[offset].dkp))
         button:Show()
      else
         button:Hide()
      end
   end
   
end


function Players:DKPHeaderOnClick(header, button)
   local newSort = { header.sorter }
   
   for i, sort in pairs(Players.UI.SortOrder) do
      if (sort ~= header.sorter) then tinsert(newSort, sort) end
   end
   
   Players.UI.SortOrder = newSort
   Players:RefilterDKPList()
end


function Players:DKPListOnClick(line, button)
   local id = line:GetID()
   local player = Players.UI.DKPList[id].player
   
   local options = { type = "group", args = { ["set_class"] = { type = "group", name = "Set Class", desc = "Set this player's class", args = {}, }, }, }
   
   local function AddClass(options, class, player, i)
      local option =
      {
         type = 'execute',
         name = string.format("|cff%s%s|r ", PandaDKPMaster:getClassHex(class), class),
         desc = class,
         func = function(data) Players:SetPlayerClass(data.player, data.class) Players:RefilterDKPList() dewdrop:Close(); end,
         order = i,
         passValue = {player = player, class = class},
      }
      tinsert(options.args["set_class"].args, option)
   end
   
   AddClass(options, 'Death Knight', player, 1)
   AddClass(options, 'Druid', player, 2)
   AddClass(options, 'Hunter', player, 3)
   AddClass(options, 'Mage', player, 4)
   AddClass(options, 'Paladin', player, 5)
   AddClass(options, 'Priest', player, 6)
   AddClass(options, 'Rogue', player, 7)
   AddClass(options, 'Shaman', player, 8)
   AddClass(options, 'Warlock', player, 9)
   AddClass(options, 'Warrior', player, 10)
   
   dewdrop:Open(line, 'children', options, 'point', "TOP", 'relativePoint', "BOTTOM")
end